//===-- CommandObjectSettings.cpp -------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CommandObjectSettings.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "llvm/ADT/StringRef.h"

// Project includes
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/OptionValueProperties.h"

using namespace lldb;
using namespace lldb_private;

//-------------------------------------------------------------------------
// CommandObjectSettingsSet
//-------------------------------------------------------------------------

class CommandObjectSettingsSet : public CommandObjectRaw
{
public:
    CommandObjectSettingsSet (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings set",
                         "Set or change the value of a single debugger setting variable.",
                         nullptr),
        m_options (interpreter)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentData var_name_arg;
        CommandArgumentData value_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first (and only) variant of this arg.
        value_arg.arg_type = eArgTypeValue;
        value_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg2.push_back (value_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
        
        SetHelpLong (
"\nWhen setting a dictionary or array variable, you can set multiple entries \
at once by giving the values to the set command.  For example:" R"(

(lldb) settings set target.run-args value1 value2 value3
(lldb) settings set target.env-vars MYPATH=~/.:/usr/bin  SOME_ENV_VAR=12345

(lldb) settings show target.run-args
  [0]: 'value1'
  [1]: 'value2'
  [3]: 'value3'
(lldb) settings show target.env-vars
  'MYPATH=~/.:/usr/bin'
  'SOME_ENV_VAR=12345'

)" "Warning:  The 'set' command re-sets the entire array or dictionary.  If you \
just want to add, remove or update individual values (or add something to \
the end), use one of the other settings sub-commands: append, replace, \
insert-before or insert-after."
        );

    }

    ~CommandObjectSettingsSet() override = default;

    // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
    bool
    WantsCompletion() override { return true; }

    Options *
    GetOptions () override
    {
        return &m_options;
    }
    
    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
            Options (interpreter),
            m_global (false)
        {
        }

        ~CommandOptions() override = default;

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;
            const int short_option = m_getopt_table[option_idx].val;

            switch (short_option)
            {
                case 'g':
                    m_global = true;
                    break;
                default:
                    error.SetErrorStringWithFormat ("unrecognized options '%c'", short_option);
                    break;
            }

            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_global = false;
        }
        
        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        // Options table: Required for subclasses of Options.

        static OptionDefinition g_option_table[];

        // Instance variables to hold the values for command options.

        bool m_global;
    };

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        const size_t argc = input.GetArgumentCount();
        const char *arg = nullptr;
        int setting_var_idx;
        for (setting_var_idx = 1; setting_var_idx < static_cast<int>(argc);
             ++setting_var_idx)
        {
            arg = input.GetArgumentAtIndex(setting_var_idx);
            if (arg && arg[0] != '-')
                break; // We found our setting variable name index
        }
        if (cursor_index == setting_var_idx)
        {
            // Attempting to complete setting variable name
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);
        }
        else
        {
            arg = input.GetArgumentAtIndex(cursor_index);
            
            if (arg)
            {
                if (arg[0] == '-')
                {
                    // Complete option name
                }
                else
                {
                    // Complete setting value
                    const char *setting_var_name = input.GetArgumentAtIndex(setting_var_idx);
                    Error error;
                    lldb::OptionValueSP value_sp (m_interpreter.GetDebugger().GetPropertyValue(&m_exe_ctx, setting_var_name, false, error));
                    if (value_sp)
                    {
                        value_sp->AutoComplete (m_interpreter,
                                                completion_str.c_str(),
                                                match_start_point,
                                                max_return_elements,
                                                word_complete,
                                                matches);
                    }
                }
            }
        }
        return matches.GetSize();
    }
    
protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        Args cmd_args(command);

        // Process possible options.
        if (!ParseOptions (cmd_args, result))
            return false;

        const size_t argc = cmd_args.GetArgumentCount ();
        if ((argc < 2) && (!m_options.m_global))
        {
            result.AppendError ("'settings set' takes more arguments");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings set' command requires a valid variable name");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        // Split the raw command into var_name and value pair.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, false, false);

        Error error;
        if (m_options.m_global)
        {
            error = m_interpreter.GetDebugger().SetPropertyValue(nullptr,
                                                                 eVarSetOperationAssign,
                                                                 var_name,
                                                                 var_value_cstr);
        }
        
        if (error.Success())
        {
            // FIXME this is the same issue as the one in commands script import
            // we could be setting target.load-script-from-symbol-file which would cause
            // Python scripts to be loaded, which could run LLDB commands
            // (e.g. settings set target.process.python-os-plugin-path) and cause a crash
            // if we did not clear the command's exe_ctx first
            ExecutionContext exe_ctx(m_exe_ctx);
            m_exe_ctx.Clear();
            error = m_interpreter.GetDebugger().SetPropertyValue (&exe_ctx,
                                                                  eVarSetOperationAssign,
                                                                  var_name,
                                                                  var_value_cstr);
        }

        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            result.SetStatus (eReturnStatusSuccessFinishResult);
        }

        return result.Succeeded();
    }

private:
    CommandOptions m_options;
};

OptionDefinition
CommandObjectSettingsSet::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_2, false, "global", 'g', OptionParser::eNoArgument,   nullptr, nullptr, 0, eArgTypeNone, "Apply the new value to the global default value." },
    { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsShow -- Show current values
//-------------------------------------------------------------------------

class CommandObjectSettingsShow : public CommandObjectParsed
{
public:
    CommandObjectSettingsShow (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "settings show",
                            "Show the specified internal debugger setting variable and its value, or show all the currently set variables and their values, if nothing is specified.",
                            nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentData var_name_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatOptional;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
    }

    ~CommandObjectSettingsShow() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eSettingsNameCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishResult);

        const size_t argc = args.GetArgumentCount ();
        if (argc > 0)
        {
            for (size_t i = 0; i < argc; ++i)
            {
                const char *property_path = args.GetArgumentAtIndex (i);

                Error error(m_interpreter.GetDebugger().DumpPropertyValue (&m_exe_ctx, result.GetOutputStream(), property_path, OptionValue::eDumpGroupValue));
                if (error.Success())
                {
                    result.GetOutputStream().EOL();
                }
                else
                {
                    result.AppendError (error.AsCString());
                    result.SetStatus (eReturnStatusFailed);
                }
            }
        }
        else
        {
            m_interpreter.GetDebugger().DumpAllPropertyValues (&m_exe_ctx, result.GetOutputStream(), OptionValue::eDumpGroupValue);
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsList -- List settable variables
//-------------------------------------------------------------------------

class CommandObjectSettingsList : public CommandObjectParsed
{
public: 
    CommandObjectSettingsList (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "settings list",
                            "List and describe all the internal debugger settings variables that are available to the user to 'set' or 'show', or describe a particular variable or set of variables (by specifying the variable name or a common prefix).",
                            nullptr)
    {
        CommandArgumentEntry arg;
        CommandArgumentData var_name_arg;
        CommandArgumentData prefix_name_arg;

        // Define the first variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatOptional;

        // Define the second variant of this arg.
        prefix_name_arg.arg_type = eArgTypeSettingPrefix;
        prefix_name_arg.arg_repetition = eArgRepeatOptional;

        arg.push_back (var_name_arg);
        arg.push_back (prefix_name_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectSettingsList() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eSettingsNameCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishResult);

        const bool will_modify = false;
        const size_t argc = args.GetArgumentCount ();
        if (argc > 0)
        {
            const bool dump_qualified_name = true;

            for (size_t i = 0; i < argc; ++i)
            {
                const char *property_path = args.GetArgumentAtIndex (i);
                
                const Property *property = m_interpreter.GetDebugger().GetValueProperties()->GetPropertyAtPath (&m_exe_ctx, will_modify, property_path);

                if (property)
                {
                    property->DumpDescription (m_interpreter, result.GetOutputStream(), 0, dump_qualified_name);
                }
                else
                {
                    result.AppendErrorWithFormat ("invalid property path '%s'", property_path);
                    result.SetStatus (eReturnStatusFailed);
                }
            }
        }
        else
        {
            m_interpreter.GetDebugger().DumpAllDescriptions (m_interpreter, result.GetOutputStream());
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsRemove
//-------------------------------------------------------------------------

class CommandObjectSettingsRemove : public CommandObjectRaw
{
public:
    CommandObjectSettingsRemove (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings remove",
                         "Remove the specified element from an array or dictionary settings variable.",
                         nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentData var_name_arg;
        CommandArgumentData index_arg;
        CommandArgumentData key_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first variant of this arg.
        index_arg.arg_type = eArgTypeSettingIndex;
        index_arg.arg_repetition = eArgRepeatPlain;

        // Define the second variant of this arg.
        key_arg.arg_type = eArgTypeSettingKey;
        key_arg.arg_repetition = eArgRepeatPlain;

        // Push both variants into this arg
        arg2.push_back (index_arg);
        arg2.push_back (key_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
    }

    ~CommandObjectSettingsRemove() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }

protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);
     
        Args cmd_args(command);
        
        // Process possible options.
        if (!ParseOptions (cmd_args, result))
            return false;
        
        const size_t argc = cmd_args.GetArgumentCount ();
        if (argc == 0)
        {
            result.AppendError ("'settings set' takes an array or dictionary item, or an array followed by one or more indexes, or a dictionary followed by one or more key names to remove");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        
        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings set' command requires a valid variable name");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        
        // Split the raw command into var_name and value pair.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false);
        
        Error error (m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx,
                                                                   eVarSetOperationRemove,
                                                                   var_name,
                                                                   var_value_cstr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        
        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsReplace
//-------------------------------------------------------------------------

class CommandObjectSettingsReplace : public CommandObjectRaw
{
public:
    CommandObjectSettingsReplace (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings replace",
                         "Replace the specified element from an internal debugger settings array or dictionary variable with the specified new value.",
                         nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentEntry arg3;
        CommandArgumentData var_name_arg;
        CommandArgumentData index_arg;
        CommandArgumentData key_arg;
        CommandArgumentData value_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first (variant of this arg.
        index_arg.arg_type = eArgTypeSettingIndex;
        index_arg.arg_repetition = eArgRepeatPlain;

        // Define the second (variant of this arg.
        key_arg.arg_type = eArgTypeSettingKey;
        key_arg.arg_repetition = eArgRepeatPlain;

        // Put both variants into this arg
        arg2.push_back (index_arg);
        arg2.push_back (key_arg);

        // Define the first (and only) variant of this arg.
        value_arg.arg_type = eArgTypeValue;
        value_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg3.push_back (value_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
        m_arguments.push_back (arg3);
    }

    ~CommandObjectSettingsReplace() override = default;

    // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
    bool
    WantsCompletion() override { return true; }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }

protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);

        Args cmd_args(command);
        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings replace' command requires a valid variable name; No value supplied");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        // Split the raw command into var_name, index_value, and value triple.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false);

        Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx,
                                                                  eVarSetOperationReplace,
                                                                  var_name,
                                                                  var_value_cstr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            result.SetStatus (eReturnStatusSuccessFinishNoResult);

        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsInsertBefore
//-------------------------------------------------------------------------

class CommandObjectSettingsInsertBefore : public CommandObjectRaw
{
public:
    CommandObjectSettingsInsertBefore (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings insert-before",
                         "Insert value(s) into an internal debugger settings array variable, immediately before the specified element.",
                         nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentEntry arg3;
        CommandArgumentData var_name_arg;
        CommandArgumentData index_arg;
        CommandArgumentData value_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first (variant of this arg.
        index_arg.arg_type = eArgTypeSettingIndex;
        index_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg2.push_back (index_arg);

        // Define the first (and only) variant of this arg.
        value_arg.arg_type = eArgTypeValue;
        value_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg3.push_back (value_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
        m_arguments.push_back (arg3);
    }

    ~CommandObjectSettingsInsertBefore() override = default;

    // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
    bool
    WantsCompletion() override { return true; }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }

protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);

        Args cmd_args(command);
        const size_t argc = cmd_args.GetArgumentCount ();

        if (argc < 3)
        {
            result.AppendError ("'settings insert-before' takes more arguments");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings insert-before' command requires a valid variable name; No value supplied");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        // Split the raw command into var_name, index_value, and value triple.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false);

        Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx,
                                                                  eVarSetOperationInsertBefore,
                                                                  var_name,
                                                                  var_value_cstr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingInsertAfter
//-------------------------------------------------------------------------

class CommandObjectSettingsInsertAfter : public CommandObjectRaw
{
public:
    CommandObjectSettingsInsertAfter (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings insert-after",
                         "Insert value(s) into an internal debugger settings array variable, immediately after the specified element.",
                         nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentEntry arg3;
        CommandArgumentData var_name_arg;
        CommandArgumentData index_arg;
        CommandArgumentData value_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first (variant of this arg.
        index_arg.arg_type = eArgTypeSettingIndex;
        index_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg2.push_back (index_arg);

        // Define the first (and only) variant of this arg.
        value_arg.arg_type = eArgTypeValue;
        value_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg3.push_back (value_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
        m_arguments.push_back (arg3);
    }

    ~CommandObjectSettingsInsertAfter() override = default;

    // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
    bool
    WantsCompletion() override { return true; }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }
    
protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);

        Args cmd_args(command);
        const size_t argc = cmd_args.GetArgumentCount ();

        if (argc < 3)
        {
            result.AppendError ("'settings insert-after' takes more arguments");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings insert-after' command requires a valid variable name; No value supplied");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        // Split the raw command into var_name, index_value, and value triple.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false);

        Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx,
                                                                  eVarSetOperationInsertAfter,
                                                                  var_name,
                                                                  var_value_cstr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsAppend
//-------------------------------------------------------------------------

class CommandObjectSettingsAppend : public CommandObjectRaw
{
public:
    CommandObjectSettingsAppend (CommandInterpreter &interpreter) :
        CommandObjectRaw(interpreter,
                         "settings append",
                         "Append a new value to the end of an internal debugger settings array, dictionary or string variable.",
                         nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentData var_name_arg;
        CommandArgumentData value_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg1.push_back (var_name_arg);

        // Define the first (and only) variant of this arg.
        value_arg.arg_type = eArgTypeValue;
        value_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg2.push_back (value_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
    }

    ~CommandObjectSettingsAppend() override = default;

    // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString.
    bool
    WantsCompletion() override { return true; }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }

protected:
    bool
    DoExecute (const char *command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);
        Args cmd_args(command);
        const size_t argc = cmd_args.GetArgumentCount ();

        if (argc < 2)
        {
            result.AppendError ("'settings append' takes more arguments");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        const char *var_name = cmd_args.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings append' command requires a valid variable name; No value supplied");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        // Do not perform cmd_args.Shift() since StringRef is manipulating the
        // raw character string later on.

        // Split the raw command into var_name and value pair.
        llvm::StringRef raw_str(command);
        std::string var_value_string = raw_str.split(var_name).second.str();
        const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false);

        Error error(m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx,
                                                                  eVarSetOperationAppend,
                                                                  var_name,
                                                                  var_value_cstr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectSettingsClear
//-------------------------------------------------------------------------

class CommandObjectSettingsClear : public CommandObjectParsed
{
public:
    CommandObjectSettingsClear (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "settings clear",
                            "Erase all the contents of an internal debugger settings variables; this is only valid for variables with clearable types, i.e. strings, arrays or dictionaries.",
                            nullptr)
    {
        CommandArgumentEntry arg;
        CommandArgumentData var_name_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeSettingVariableName;
        var_name_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (var_name_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectSettingsClear() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position);

        // Attempting to complete variable name
        if (cursor_index < 2)
            CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                                CommandCompletions::eSettingsNameCompletion,
                                                                completion_str.c_str(),
                                                                match_start_point,
                                                                max_return_elements,
                                                                nullptr,
                                                                word_complete,
                                                                matches);

        return matches.GetSize();
    }

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);
        const size_t argc = command.GetArgumentCount ();

        if (argc != 1)
        {
            result.AppendError ("'settings clear' takes exactly one argument");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        const char *var_name = command.GetArgumentAtIndex (0);
        if ((var_name == nullptr) || (var_name[0] == '\0'))
        {
            result.AppendError ("'settings clear' command requires a valid variable name; No value supplied");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        
        Error error(m_interpreter.GetDebugger().SetPropertyValue(&m_exe_ctx,
                                                                 eVarSetOperationClear,
                                                                 var_name,
                                                                 nullptr));
        if (error.Fail())
        {
            result.AppendError (error.AsCString());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        return result.Succeeded();
    }
};

//-------------------------------------------------------------------------
// CommandObjectMultiwordSettings
//-------------------------------------------------------------------------

CommandObjectMultiwordSettings::CommandObjectMultiwordSettings (CommandInterpreter &interpreter) :
    CommandObjectMultiword (interpreter,
                            "settings",
                            "A set of commands for manipulating internal settable debugger variables.",
                            "settings <command> [<command-options>]")
{
    LoadSubCommand ("set",           CommandObjectSP (new CommandObjectSettingsSet (interpreter)));
    LoadSubCommand ("show",          CommandObjectSP (new CommandObjectSettingsShow (interpreter)));
    LoadSubCommand ("list",          CommandObjectSP (new CommandObjectSettingsList (interpreter)));
    LoadSubCommand ("remove",        CommandObjectSP (new CommandObjectSettingsRemove (interpreter)));
    LoadSubCommand ("replace",       CommandObjectSP (new CommandObjectSettingsReplace (interpreter)));
    LoadSubCommand ("insert-before", CommandObjectSP (new CommandObjectSettingsInsertBefore (interpreter)));
    LoadSubCommand ("insert-after",  CommandObjectSP (new CommandObjectSettingsInsertAfter (interpreter)));
    LoadSubCommand ("append",        CommandObjectSP (new CommandObjectSettingsAppend (interpreter)));
    LoadSubCommand ("clear",         CommandObjectSP (new CommandObjectSettingsClear (interpreter)));
}

CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default;
